/*
 * BPlusTreeTest.cpp
 *
 */

#include "BPlusTreeTest.h"
#include "../../src/logical_layer/bplustree/BPlusTree.h"
#include "../../src/physical_layer/PersistorBase.h"
#include "../../src/logical_layer/bplustree/PersistorPool.h"
#include "../../src/utils/ConfigurationMannagerPool.h"
#include "../../src/utils/StringUtils.h"
#include "../../src/logical_layer/bplustree/ElementFactory.h"

using namespace std;
BPlusTreeTest::BPlusTreeTest() {
	this->bplus = NULL;
}

void BPlusTreeTest::run() {
	cout << "\n=== BPlusTree Tests ===\n";

	try {
		testSimpleElements();
		testTreeElement();
		testSequentialWay();
		PersistorPool::dropInstance(ELEMENT);
		std::cout << "\nAll tests OK\n";
	} catch (std::exception& e) {
		std::cout << "Test Fail: " << e.what() << endl;
	}
}

void BPlusTreeTest::testSimpleElements() {
	if (this->bplus != NULL) {
		delete this->bplus;
		PersistorPool::dropInstance(ELEMENT);
	}
	cout << " TESTING testSimpleElements" << endl;
	system("rm testTree");
	system("rm testTree.fs");
	this->bplus =
			new BPlusTree(
					ConfigurationMannagerPool::getInstance(ELEMENT)->getBufferSizeTree(),
					"testTree", ELEMENT);

	for (int i = 1; i < 156; i++) {
//		bplus->exportTree();
		bplus->insert(ElementFactory::createElement(ELEMENT, i, "4xxx", 4));
//		cout << "(" << i << ";" << "4xxx" << ")" << endl;
	}

	for (int i = 1; i < 156; i++) {
/*
		string mensaje = "Bplus test eliminando clave: ";
		mensaje.append(StringUtils::convertIntToString(i));
		cout << mensaje << endl;
*/
		bplus->remove(GenericKey((ID) i));

	}

}
void BPlusTreeTest::testSequentialWay() {
	if (this->bplus != NULL) {
		delete this->bplus;
		PersistorPool::dropInstance(ELEMENT);
	}
	cout
			<< "========================Iniciando el test de recorrido sequencial============="
			<< endl;
	system("rm testTree");
	system("rm testTree.fs");
	this->bplus =
			new BPlusTree(
					ConfigurationMannagerPool::getInstance(ELEMENT)->getBufferSizeTree(),
					"testTree", ELEMENT);

	for (int i = 1; i < 5000; i++) {
		bplus->insert(ElementFactory::createElement(ELEMENT, i, "4xxx", 4));
//		cout << "(" << i << ";" << "4xxx" << ")" << endl;
	}

//	bplus->exportTree();
	LeafNode* node = (LeafNode*) bplus->next();
	while (node != NULL) {
//		node->exportNode();
		// no hago delete porque se lo pido al arbol. El arbol es el duenno de los nodos que pido
		node = (LeafNode*) bplus->next();
		//cout<<"leafNode: prev node "<<node->getPrevNode()<<endl;
		//cout<<"leafNode: next node "<<node->getNextNode()<<endl;


	}

}

void BPlusTreeTest::testTreeElement() {
	if (this->bplus != NULL) {
		delete this->bplus;
		PersistorPool::dropInstance(ELEMENT);
	}
	cout << " TESTING testTreeElement" << endl;
	system("rm testTree");
	system("rm testTree.fs");
	this->bplus =
			new BPlusTree(
					ConfigurationMannagerPool::getInstance(ELEMENT)->getBufferSizeTree(),
					"testTree", ELEMENT);

	for (int i = 1; i < 5000; i++) {
		bplus->insert(ElementFactory::createElement(ELEMENT, i, "4xxx", 4));
//		cout << "(" << i << ";" << "4xxx" << ")" << endl;
	}

	for (int i = 1; i < 5000; i++) {
		string mensaje = "Bplus test eliminando clave: ";
		mensaje.append(StringUtils::convertIntToString(i));
	//	cout << mensaje << endl;

		bplus->remove(GenericKey((ID) i));
	}

	cout << "***************************************" << endl;
	cout << "Ejecutando el primer set de test" << endl;
	cout << "***************************************" << endl;
	this->firstSetTest();
	cout << "***************************************" << endl;
	cout << "Ejecutando el segundo set de test" << endl;
	cout << "***************************************" << endl;
	this->secondSetTest();
	cout << "***************************************" << endl;
	cout << "Ejecutando el tercer set de test" << endl;
	cout << "***************************************" << endl;
	this->thirdSetTest();
	cout << " TESTING testTreeElement OK" << endl;
}

void BPlusTreeTest::firstSetTest() {
	delete this->bplus;
	PersistorPool::dropInstance(ELEMENT);
	system("rm testTree");
	system("rm testTree.fs");
	this->bplus =
			new BPlusTree(
					ConfigurationMannagerPool::getInstance(ELEMENT)->getBufferSizeTree(),
					"testTree", ELEMENT);
	BPlusTree& bplus = *this->bplus;
	int i;
	cout
			<< "Insertamos los elementos 100, 101, 102,103,104 todos con 4 bytes de datos: total de cada registro en el nodo hoja= 12 bytes"
			<< endl;
	for (i = 100; i < 105; i++) {
//		cout << "Insertando el elemento: " << i << endl;
		bplus.insert(ElementFactory::createElement(ELEMENT, i, "4xxx", 4));
		//bplus.exportTree();
		//getchar();
	}
	cout << endl;

//	bplus.exportTree();

	cout << "Buscando el elemento 104" << endl;

	Element& element104 = *(Element*) bplus.findExact(GenericKey((ID) 104));
	cout << "************Elemento encontrado: " << element104 << endl << endl;
	Element& element102 = *(Element*) bplus.findExact(GenericKey((ID) 102));
	cout << "************Elemento encontrado: " << element102 << endl << endl;

	cout << "Modificando elemento elmentos 101 y 103 sin split" << endl;
	bplus.modify(
			ElementFactory::createElement(ELEMENT, 101, "12xxxxxxxxxx", 12));
//	bplus.exportTree();
	bplus.modify(
			ElementFactory::createElement(ELEMENT, 102, "12xxxxxxxxxx", 12));
//	bplus.exportTree();
//	bplus.exportTree();

	cout << "Modificando elemento elmento 103 generando split" << endl;
	bplus.modify(ElementFactory::createElement(ELEMENT, 103, "9xxxxxxxx", 9));
//	bplus.exportTree();
	cout << "Eliminamos el elemento 103" << endl;
	bplus.remove(GenericKey((ID) 103));
//	bplus.exportTree();
	cout << "Modificamos el elemento 102" << endl;
	bplus.modify(ElementFactory::createElement(ELEMENT, 102, "1", 1));
//	bplus.exportTree();
	cout << "eliminamos la clave 104 y se produce un balanceo" << endl;
	bplus.remove(GenericKey((ID) 104));
//	bplus.exportTree();
	cout << "eliminamos la clave 100" << endl;
	bplus.remove(GenericKey((ID) 100));
//	bplus.exportTree();
	cout << "eliminamos la clave 101" << endl;
	bplus.remove(GenericKey((ID) 101));
//	bplus.exportTree();
	cout << "eliminamos la clave 102" << endl;
	bplus.remove(GenericKey((ID) 102));
//	bplus.exportTree();

	cout << "end..........." << endl;

}

void BPlusTreeTest::secondSetTest() {
	system("rm testTree");
	system("rm testTree.fs");
	BPlusTree& bplus = *this->bplus;
	int i;
	cout
			<< "Insertamos los elementos 100, 101, 102,103,104 todos con 4 bytes de datos: total de cada registro en el nodo hoja= 12 bytes"
			<< endl;
	for (i = 100; i < 105; i++) {
//		cout << "Insertando el elemento: " << i << endl;
		bplus.insert(ElementFactory::createElement(ELEMENT, i, "4xxx", 4));
		//bplus.exportTree();
		//getchar();
	}
	cout << endl;

//	bplus.exportTree();

	cout << "Buscando el elemento 104" << endl;
	IElement* element = bplus.findExact(GenericKey((ID) 104));
	cout << "************Elemento encontrado: " << element << endl << endl;

	cout << "Modificando elemento elmentos 101 y 103 sin split" << endl;
	bplus.modify(
			ElementFactory::createElement(ELEMENT, 101, "12xxxxxxxxxx", 12));
	bplus.modify(
			ElementFactory::createElement(ELEMENT, 102, "12xxxxxxxxxx", 12));
//	bplus.exportTree();

	cout << "Modificando elemento elmento 103 generando split" << endl;
	bplus.modify(ElementFactory::createElement(ELEMENT, 103, "9xxxxxxxx", 9));
//	bplus.exportTree();
	cout << "Eliminamos el elemento 103" << endl;
	bplus.remove(GenericKey((ID) 103));
//	bplus.exportTree();
	cout << "Modificamos el elemento 102" << endl;
	bplus.modify(ElementFactory::createElement(ELEMENT, 102, "1", 1));
//	bplus.exportTree();
	cout << "eliminamos la clave 104 y se produce un balanceo" << endl;
	bplus.remove(GenericKey((ID) 104));
//	bplus.exportTree();
	cout << "reinsertamos la clave 104 para ver que si inserta correctamente"
			<< endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 104, "4xxx", 4));
//	bplus.exportTree();
	cout << "eliminamos la clave 100" << endl;
	bplus.remove(GenericKey((ID) 100));
//	bplus.exportTree();
	cout
			<< "Modificamos el elemento 101 para que quede en overflow y le pida al hermano derecho"
			<< endl;
	bplus.modify(ElementFactory::createElement(ELEMENT, 101, "1", 1));
//	bplus.exportTree();
	cout << "eliminamos la clave 101" << endl;
	bplus.remove(GenericKey((ID) 101));
//	bplus.exportTree();
	cout << "eliminamos la clave 102" << endl;
	bplus.remove(GenericKey((ID) 102));
//	bplus.exportTree();
	cout << "eliminamos la clave 104" << endl;
	bplus.remove(GenericKey((ID) 104));
//	bplus.exportTree();

	cout << "end..........." << endl;

}

void BPlusTreeTest::thirdSetTest() {
	cout
			<< "probamos los balanceos en los nodos internos e insercciones intermedias"
			<< endl;

	BPlusTree& bplus = *this->bplus;

	cout << "Insertamos 5 elementos de longuitud 4" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 100, "4xxx", 4));
	bplus.insert(ElementFactory::createElement(ELEMENT, 105, "4xxx", 4));
	bplus.insert(ElementFactory::createElement(ELEMENT, 110, "4xxx", 4));
	bplus.insert(ElementFactory::createElement(ELEMENT, 115, "4xxx", 4));
	bplus.insert(ElementFactory::createElement(ELEMENT, 120, "4xxx", 4));
//	bplus.exportTree();

	cout << "Insertando el 101 con long 4" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 101, "4xxx", 4));
//	bplus.exportTree();
	cout << "Insertando el 103 con long 4" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 103, "4xxx", 4));
//	bplus.exportTree();
	cout << "Insertando el 106 con long 4" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 106, "4xxx", 4));
//	bplus.exportTree();
	cout << "Insertando el 112 con long 4" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 112, "4xxx", 4));
//	bplus.exportTree();
	cout
			<< "Insertando el 107 con long 4 debe splitear la hoja y generar un nodo interno mas"
			<< endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 107, "4xxx", 4));
//	bplus.exportTree();

	cout << "Insertamos elementos a la izquierda" << endl;
	cout << "Insertando el 99 con long 4" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 99, "4xxx", 4));
//	bplus.exportTree();
	cout << "Buscando Elemento 100: " << bplus.findExact(GenericKey((ID) 99))
			<< endl;

	cout << "Insertando el 98 con long 4" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 98, "4xxx", 4));
//	bplus.exportTree();

	cout << "Insertando el 104 con long 4 debe generar un split hacia arriba"
			<< endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 104, "4xxx", 4));
//	bplus.exportTree();

	cout << "Insertando el 108 " << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 108, "4xxx", 4));
//	bplus.exportTree();

	cout << "Insertando el 109 " << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 109, "4xxx", 4));
//	bplus.exportTree();

	cout << "Insertando el 121 " << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 121, "4xxx", 4));
//	bplus.exportTree();

	cout << "Insertando el 122 " << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 122, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	cout << "Insertando el 123 " << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 123, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	cout << "Insertando el 97 " << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 97, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	cout << "Insertando el 96 " << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 96, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	cout
			<< "Insertando el 95 Esto genera un split de root y un nivel mas de nodos internos"
			<< endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 95, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	cout << "Insertando el 124" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 124, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	cout << "Insertando el 125" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 125, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	cout << "Insertando el 126" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, 126, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	int i;
	for (i = 127; i < 141; i++) {
//		cout << "Insertando el" << i << " " << endl;
		bplus.insert(ElementFactory::createElement(ELEMENT, i, "4xxx", 4));
//		bplus.exportTree();
//		cout << "end..........." << endl;
	}

	cout << "Insertando el" << i << " Esto debe generar un split" << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, i, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	cout << "Insertamos elementos para que desborde el nodo interno" << endl;

	for (i = 142; i < 162; i++) {
//		cout << "Insertando el" << i << " " << endl;
		bplus.insert(ElementFactory::createElement(ELEMENT, i, "4xxx", 4));
//		bplus.exportTree();
//		cout << "end..........." << endl;
	}

	cout << "Insertando el" << i << " esto genera un split " << endl;
	bplus.insert(ElementFactory::createElement(ELEMENT, i, "4xxx", 4));
//	bplus.exportTree();
	cout << "end..........." << endl;

	cout << "********************************" << endl;
	cout << "Eliminamos el utimo nodo " << endl;
	cout << "********************************" << endl;
	bplus.remove(GenericKey((ID) 160));
//	bplus.exportTree();
	bplus.remove(GenericKey((ID) 161));
//	bplus.exportTree();
	cout
			<< "********************Elimino el 162 y me genera un revalanceo***********************"
			<< endl;
	bplus.remove(GenericKey((ID) 162));
//	bplus.exportTree();
	cout
			<< "********************Elimino el 159 y me genera un revalanceo***********************"
			<< endl;
	bplus.remove(GenericKey((ID) 159));
//	bplus.exportTree();

	cout
			<< "********************Elimino el 158 y elimina el ultimo nodo***********************"
			<< endl;
	bplus.remove(GenericKey((ID) 158));
//	bplus.exportTree();

	cout << "********************************" << endl;
	cout << "Eliminamos el otro nodo " << endl;
	cout << "********************************" << endl;

	for (i = 154; i < 157; i++) {
//		cout << "Eliminando " << i << endl;
		bplus.remove(GenericKey((ID) i));
//		bplus.exportTree();
	}

	cout
			<< "********************Elimino el 158 y genera un contraccion del nodo***********************"
			<< endl;
	bplus.remove(GenericKey((ID) 157));
//	bplus.exportTree();

	cout << "********************************" << endl;
	cout << "Eliminamos el otro nodo " << endl;
	cout << "********************************" << endl;

	for (i = 150; i < 153; i++) {
//		cout << "Eliminando " << i << endl;
		ID id = i;
		bplus.remove(GenericKey(id));
//		bplus.exportTree();
	}

	cout
			<< "********************Elimino el 153 y genera un contraccion del nodo***********************"
			<< endl;
	bplus.remove(GenericKey((ID) 153));
//	bplus.exportTree();

	//modificamos un nodo
	bplus.modify(ElementFactory::createElement(ELEMENT, 141, "5xxxx", 5));
//	bplus.exportTree();

	cout << "********************************" << endl;
	cout << "Eliminamos el otro nodo " << endl;
	cout << "********************************" << endl;

	for (i = 147; i < 149; i++) {
//		cout << "Eliminando " << i << endl;
		bplus.remove(GenericKey((ID) i));
//     	bplus.exportTree();
	}

	cout
			<< "********************Elimino el 149 y genera un contraccion del nodo***********************"
			<< endl;
	bplus.remove(GenericKey((ID) 149));
//	bplus.exportTree();

	cout << "eliminamos el registro 101" << endl;

	bplus.remove(GenericKey((ID) 101));
//	bplus.exportTree();

	cout << "********************************" << endl;
	cout << "Eliminamos el otro nodo " << endl;
	cout << "********************************" << endl;

	for (i = 120; i < 135; i++) {
//		cout << "Eliminando " << i << endl;
		bplus.remove(GenericKey((ID) i));
//		bplus.exportTree();
	}
	cout << "Eliminando " << i << endl;
	bplus.remove(GenericKey((ID) i));

	cout << "Eliminando " << 140 << endl;
	bplus.remove(GenericKey((ID) 140));
//	bplus.exportTree();

	for (i = 144; i < 146; i++) {
//		cout << "Eliminando " << i << endl;
		bplus.remove(GenericKey((ID) i));
//		bplus.exportTree();
	}

	cout
			<< "********************Elimino el 146 y genera un contraccion del nodo***********************"
			<< endl;
	bplus.remove(GenericKey((ID) 146));
//	bplus.exportTree();

	cout << "********************************" << endl;
	cout << "Eliminamos el otro nodo " << endl;
	cout << "********************************" << endl;

	for (i = 141; i < 143; i++) {
//		cout << "Eliminando " << i << endl;
		bplus.remove(GenericKey((ID) i));
//		bplus.exportTree();
	}

	cout
			<< "********************Elimino el 143 y genera un contraccion del nodo***********************"
			<< endl;
	bplus.remove(GenericKey((ID) 143));
//	bplus.exportTree();

	cout << "********************************" << endl;
	cout
			<< "Eliminamos mas nodos para poder realizar una contraccion de un nodo hoja "
			<< endl;
	cout << "********************************" << endl;
	for (i = 137; i < 139; i++) {
//		cout << "Eliminando " << i << endl;
		bplus.remove(GenericKey((ID) i));
//		bplus.exportTree();
	}
	cout << "Eliminando " << i << endl;
	bplus.remove(GenericKey((ID) i));
//	bplus.exportTree();

	cout << "FIN EXITOSO!!!!!!!!!" << endl;
}

BPlusTreeTest::~BPlusTreeTest() {
}
